/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#define LOG_TAG "pn54x"

#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/time.h>

#include <log/log.h>

#include <phNxpExtns_MifareStd.h>
#include <phNxpLog.h>
#include <phNxpConfig.h>

phNxpExtns_Context_t         gphNxpExtns_Context;
extern phFriNfc_NdefMap_t    *NdefMap;
extern phNci_mfc_auth_cmd_t  gAuthCmdBuf;

static NFCSTATUS phNxpExtns_ProcessSysMessage (phLibNfc_Message_t *msg);
static NFCSTATUS phNxpExtns_SendMsg (phLibNfc_Message_t *sysmsg);

/*******************************************************************************
**
** Function         EXTNS_Init
**
** Description      This function Initializes Mifare Classic Extns. Allocates
**                  required memory and initializes the Mifare Classic Vars
**
** Returns          NFCSTATUS_SUCCESS if successfully initialized
**                  NFCSTATUS_FAILED otherwise
**
*******************************************************************************/
NFCSTATUS EXTNS_Init (tNFA_DM_CBACK        *p_nfa_dm_cback,
                      tNFA_CONN_CBACK      *p_nfa_conn_cback)
{
    NFCSTATUS status = NFCSTATUS_FAILED;

    /* reset config cache */
    resetNxpConfig ();

    /* Initialize Log level */
    phNxpLog_InitializeLogLevel ();

    /* Validate parameters */
    if ((!p_nfa_dm_cback) || (!p_nfa_conn_cback))
    {
        NXPLOG_EXTNS_E ("EXTNS_Init(): error null callback");
        goto clean_and_return;
    }

    gphNxpExtns_Context.p_dm_cback = p_nfa_dm_cback;
    gphNxpExtns_Context.p_conn_cback = p_nfa_conn_cback;

    if (NFCSTATUS_SUCCESS != phNxpExtns_MfcModuleInit ())
    {
       NXPLOG_EXTNS_E ("ERROR: MFC Module Init Failed");
       goto clean_and_return;
    }
    gphNxpExtns_Context.Extns_status = EXTNS_STATUS_OPEN;

    status = NFCSTATUS_SUCCESS;
    return status;

clean_and_return:
    gphNxpExtns_Context.Extns_status = EXTNS_STATUS_CLOSE;
    return status;
}

/*******************************************************************************
**
** Function         EXTNS_Close
**
** Description      This function de-initializes Mifare Classic Extns.
**                  De-allocates memory
**
** Returns          None
**
*******************************************************************************/
void EXTNS_Close (void)
{
    gphNxpExtns_Context.Extns_status = EXTNS_STATUS_CLOSE;
    phNxpExtns_MfcModuleDeInit ();
    return;
}

/*******************************************************************************
**
** Function         EXTNS_MfcCallBack
**
** Description      Decodes Mifare Classic Tag Response
**                  This is called from NFA_SendRaw Callback
**
** Returns:
**                  NFCSTATUS_SUCCESS if successfully initiated
**                  NFCSTATUS_FAILED otherwise
**
*******************************************************************************/
NFCSTATUS EXTNS_MfcCallBack (uint8_t *buf, uint32_t buflen)
{
    NFCSTATUS status = NFCSTATUS_SUCCESS;

    phLibNfc_Message_t msg;

    msg.eMsgType = PH_NXPEXTNS_RX_DATA;
    msg.pMsgData = buf;
    msg.Size = buflen;

    status = phNxpExtns_SendMsg (&msg);
    if (NFCSTATUS_SUCCESS != status)
    {
        NXPLOG_EXTNS_E ("Error Sending msg to Extension Thread");
    }
    return status;
}

/*******************************************************************************
**
** Function         EXTNS_MfcCheckNDef
**
** Description      Performs NDEF detection for Mifare Classic Tag
**
**                  Upon successful completion of NDEF detection, a
**                  NFA_NDEF_DETECT_EVT will be sent, to notify the application
**                  of the NDEF attributes (NDEF total memory size, current
**                  size, etc.).
**
** Returns:
**                  NFCSTATUS_SUCCESS if successfully initiated
**                  NFCSTATUS_FAILED otherwise
**
*******************************************************************************/
NFCSTATUS EXTNS_MfcCheckNDef (void)
{
    NFCSTATUS status = NFCSTATUS_SUCCESS;
    phLibNfc_Message_t msg;

    msg.eMsgType = PH_NXPEXTNS_MIFARE_CHECK_NDEF;
    msg.pMsgData = NULL;
    msg.Size = 0;

    status = phNxpExtns_SendMsg (&msg);
    if (NFCSTATUS_SUCCESS != status)
    {
        NXPLOG_EXTNS_E ("Error Sending msg to Extension Thread");
    }

    return status;
}

/*******************************************************************************
**
** Function         EXTNS_MfcReadNDef
**
** Description      Reads NDEF message from Mifare Classic Tag.
**
**                  Upon receiving the NDEF message, the message will be sent to
**                  the handler registered with EXTNS_MfcRegisterNDefTypeHandler.
**
** Returns:
**                  NFCSTATUS_SUCCESS if successfully initiated
**                  NFCSTATUS_FAILED otherwise
**
*******************************************************************************/
NFCSTATUS EXTNS_MfcReadNDef (void)
{
    NFCSTATUS status = NFCSTATUS_SUCCESS;

    phLibNfc_Message_t msg;

    msg.eMsgType = PH_NXPEXTNS_MIFARE_READ_NDEF;
    msg.pMsgData = NULL;
    msg.Size = 0;

    status = phNxpExtns_SendMsg (&msg);
    if (NFCSTATUS_SUCCESS != status)
    {
        NXPLOG_EXTNS_E("Error Sending msg to Extension Thread");
    }

    return status;
}
/*******************************************************************************
**
** Function         EXTNS_MfcPresenceCheck
**
** Description      Do the check presence for Mifare Classic Tag.
**
**
** Returns:
**                  NFCSTATUS_SUCCESS if successfully initiated
**                  NFCSTATUS_FAILED otherwise
**
*******************************************************************************/
NFCSTATUS EXTNS_MfcPresenceCheck (void)
{
    NFCSTATUS status = NFCSTATUS_SUCCESS;

    phLibNfc_Message_t msg;

    msg.eMsgType = PH_NXPEXTNS_MIFARE_PRESENCE_CHECK;
    msg.pMsgData = NULL;
    msg.Size = 0;

    gAuthCmdBuf.status = NFCSTATUS_FAILED;
    if (sem_init (&gAuthCmdBuf.semPresenceCheck, 0, 0) == -1)
    {
        ALOGE("%s: semaphore creation failed (errno=%d)", __func__, errno);
        return NFCSTATUS_FAILED;
    }

    status = phNxpExtns_SendMsg (&msg);
    if (NFCSTATUS_SUCCESS != status)
    {
        NXPLOG_EXTNS_E ("Error Sending msg to Extension Thread");
        sem_destroy (&gAuthCmdBuf.semPresenceCheck);
    }

    return status;
}

/*******************************************************************************
**
** Function        EXTNS_MfcSetReadOnly
**
**
** Description:
**      Sets tag as read only.
**
**      When tag is set as read only, or if an error occurs, the app will be
**      notified with NFA_SET_TAG_RO_EVT.
**
** Returns:
**                  NFCSTATUS_SUCCESS if successfully initiated
**                  NFCSTATUS_FAILED otherwise
**
*******************************************************************************/
NFCSTATUS EXTNS_MfcSetReadOnly (uint8_t *key, uint8_t len)
{
    NFCSTATUS status = NFCSTATUS_SUCCESS;

    phLibNfc_Message_t msg;

    msg.eMsgType = PH_NXPEXTNS_MIFARE_READ_ONLY;
    msg.pMsgData = key;
    msg.Size = len;

    status = phNxpExtns_SendMsg (&msg);
    if (NFCSTATUS_SUCCESS != status)
    {
        NXPLOG_EXTNS_E ("Error Sending msg to Extension Thread");
    }

    return status;
}

/*******************************************************************************
**
** Function         EXTNS_MfcWriteNDef
**
** Description      Writes NDEF data to Mifare Classic Tag.
**
**                  When the entire message has been written, or if an error
**                  occurs, the app will be notified with NFA_WRITE_CPLT_EVT.
**
**                  p_data needs to be persistent until NFA_WRITE_CPLT_EVT
**
** Returns:
**                  NFCSTATUS_SUCCESS if successfully initiated
**                  NFCSTATUS_FAILED otherwise
**
*******************************************************************************/
NFCSTATUS EXTNS_MfcWriteNDef (uint8_t *p_data, uint32_t len)
{
    NFCSTATUS status = NFCSTATUS_SUCCESS;

    phLibNfc_Message_t msg;

    msg.eMsgType = PH_NXPEXTNS_MIFARE_WRITE_NDEF;
    msg.pMsgData = p_data;
    msg.Size = len;

    status = phNxpExtns_SendMsg (&msg);
    if (NFCSTATUS_SUCCESS != status)
    {
        NXPLOG_EXTNS_E ("Error Sending msg to Extension Thread");
    }

    return status;
}

/*****************************************************************************
**
** Function         EXTNS_MfcFormatTag
**
** Description      Formats Mifare Classic Tag.
**
**                  The NFA_RW_FORMAT_CPLT_EVT, status is used to
**                  indicate if tag is successfully formated or not
**
** Returns
**                  NFCSTATUS_SUCCESS if successfully initiated
**                  NFCSTATUS_FAILED otherwise
**
*****************************************************************************/
NFCSTATUS EXTNS_MfcFormatTag (uint8_t *key, uint8_t len)
{
    NFCSTATUS status = NFCSTATUS_SUCCESS;

    phLibNfc_Message_t msg;

    msg.eMsgType = PH_NXPEXTNS_MIFARE_FORMAT_NDEF;
    msg.pMsgData = key;
    msg.Size = len;

    status = phNxpExtns_SendMsg (&msg);
    if (NFCSTATUS_SUCCESS != status)
    {
        NXPLOG_EXTNS_E ("Error Sending msg to Extension Thread");
    }

    return status;
}

/*****************************************************************************
**
** Function         EXTNS_MfcDisconnect
**
** Description      Disconnects Mifare Classic Tag.
**
** Returns
**                  NFCSTATUS_SUCCESS if successfully initiated
**                  NFCSTATUS_FAILED otherwise
**
*****************************************************************************/
NFCSTATUS EXTNS_MfcDisconnect (void)
{
    NFCSTATUS status = NFCSTATUS_SUCCESS;

    phLibNfc_Message_t msg;

    msg.eMsgType = PH_NXPEXTNS_DISCONNECT;
    msg.pMsgData = NULL;
    msg.Size = 0;

    status = phNxpExtns_SendMsg (&msg);
    if (NFCSTATUS_SUCCESS != status)
    {
        NXPLOG_EXTNS_E ("Error Sending msg to Extension Thread");
    }

    return status;
}

/*****************************************************************************
**
** Function         EXTNS_MfcActivated
**
** Description      Activates Mifare Classic Tag.
**
** Returns
**                  NFCSTATUS_SUCCESS if successfully initiated
**                  NFCSTATUS_FAILED otherwise
**
*****************************************************************************/
NFCSTATUS EXTNS_MfcActivated (void)
{
    NFCSTATUS status = NFCSTATUS_SUCCESS;
    phLibNfc_Message_t msg;

    msg.eMsgType = PH_NXPEXTNS_ACTIVATED;
    msg.pMsgData = NULL;
    msg.Size = 0;

    status = phNxpExtns_SendMsg (&msg);
    if (NFCSTATUS_SUCCESS != status)
    {
        NXPLOG_EXTNS_E ("Error Sending msg to Extension Thread");
    }

    return status;
}

/*******************************************************************************
**
** Function         EXTNS_MfcTransceive
**
** Description      Sends raw frame to Mifare Classic Tag.
**
** Returns          NFCSTATUS_SUCCESS if successfully initiated
**                  NFCSTATUS_FAILED otherwise
**
*******************************************************************************/
NFCSTATUS EXTNS_MfcTransceive (uint8_t *p_data, uint32_t len)
{
    NFCSTATUS status = NFCSTATUS_SUCCESS;

    phLibNfc_Message_t msg;

    msg.eMsgType = PH_NXPEXTNS_MIFARE_TRANSCEIVE;
    msg.pMsgData = p_data;
    msg.Size = len;

    status = phNxpExtns_SendMsg (&msg);
    if (NFCSTATUS_SUCCESS != status)
    {
        NXPLOG_EXTNS_E ("Error Sending msg to Extension Thread");
    }

    return status;
}

/*******************************************************************************
**
** Function         EXTNS_MfcInit
**
** Description      This function is used to Init Mifare Classic Extns.
**                  This function should be called when the tag detected is
**                  Mifare Classic.
**
** Returns          NFCSTATUS_SUCCESS
**
*******************************************************************************/
NFCSTATUS EXTNS_MfcInit (tNFA_ACTIVATED activationData)
{
    tNFC_ACTIVATE_DEVT rfDetail = activationData.activate_ntf;

    NdefMap->psRemoteDevInfo->RemoteDevInfo.Iso14443A_Info.Sak     = rfDetail.rf_tech_param.param.pa.sel_rsp;
    NdefMap->psRemoteDevInfo->RemoteDevInfo.Iso14443A_Info.AtqA [0] = rfDetail.rf_tech_param.param.pa.sens_res[0];
    NdefMap->psRemoteDevInfo->RemoteDevInfo.Iso14443A_Info.AtqA [1] = rfDetail.rf_tech_param.param.pa.sens_res[1];

    return NFCSTATUS_SUCCESS;
}

/*******************************************************************************
**
** Function         phNxpExtns_ProcessSysMessage
**
** Description      Internal function to route the request from JNI and Callback
**                  from NFA_SendRawFrame to right function
**
** Returns          NFCSTATUS_SUCCESS if valid request
**                  NFCSTATUS_FAILED otherwise
**
*******************************************************************************/
static NFCSTATUS phNxpExtns_ProcessSysMessage (phLibNfc_Message_t *msg)
{
    NFCSTATUS status = NFCSTATUS_SUCCESS;

    if (gphNxpExtns_Context.Extns_status == EXTNS_STATUS_CLOSE)
    {
        return NFCSTATUS_FAILED;
    }

    switch (msg->eMsgType)
    {
        case PH_NXPEXTNS_RX_DATA:
            status = Mfc_RecvPacket (msg->pMsgData, msg->Size);
            break;

        case PH_NXPEXTNS_MIFARE_CHECK_NDEF:
            pthread_mutex_init (&gAuthCmdBuf.syncmutex, NULL);
            pthread_mutex_lock (&gAuthCmdBuf.syncmutex);
            status = Mfc_CheckNdef ();
            pthread_mutex_unlock (&gAuthCmdBuf.syncmutex);
            pthread_mutex_destroy (&gAuthCmdBuf.syncmutex);
            break;

        case PH_NXPEXTNS_MIFARE_READ_NDEF:
            status = Mfc_ReadNdef ();
            break;

        case PH_NXPEXTNS_MIFARE_WRITE_NDEF:
            status = Mfc_WriteNdef (msg->pMsgData, msg->Size);
            break;

        case PH_NXPEXTNS_MIFARE_FORMAT_NDEF:
            status = Mfc_FormatNdef (msg->pMsgData, msg->Size);
            break;

        case PH_NXPEXTNS_DISCONNECT:
            Mfc_DeactivateCbackSelect ();
            break;

        case PH_NXPEXTNS_ACTIVATED:
            Mfc_ActivateCback ();
            break;

        case PH_NXPEXTNS_MIFARE_TRANSCEIVE:
            status = Mfc_Transceive (msg->pMsgData, msg->Size);
            break;

        case PH_NXPEXTNS_MIFARE_READ_ONLY:
            status = Mfc_SetReadOnly (msg->pMsgData, msg->Size);
            break;
        case PH_NXPEXTNS_MIFARE_PRESENCE_CHECK:
            pthread_mutex_init (&gAuthCmdBuf.syncmutex, NULL);
            pthread_mutex_lock (&gAuthCmdBuf.syncmutex);
            status = Mfc_PresenceCheck ();
            pthread_mutex_unlock (&gAuthCmdBuf.syncmutex);
            pthread_mutex_destroy (&gAuthCmdBuf.syncmutex);
            break;
        default:
            status = NFCSTATUS_FAILED;
            NXPLOG_EXTNS_E ("Illegal Command for Extension");
            break;
        }

    return status;
}

/*******************************************************************************
**
** Function         phNxpExtns_SendMsg
**
** Description      unlocks phNxpExtns_ProcessSysMessage with a valid message
**
** Returns          NFCSTATUS_SUCCESS if successfully initiated
**                  NFCSTATUS_FAILED otherwise
**
*******************************************************************************/
static NFCSTATUS phNxpExtns_SendMsg (phLibNfc_Message_t *sysmsg)
{
    NFCSTATUS status = NFCSTATUS_SUCCESS;

    status = phNxpExtns_ProcessSysMessage (sysmsg);

    return status;
}

/*******************************************************************************
**
** Function         EXTNS_MfcRegisterNDefTypeHandler
**
** Description      This function allows the applications to register for
**                  specific types of NDEF records.
**
**                  For records types which were not registered, the record will
**                  be sent to the default handler.
**
** Returns          NFCSTATUS_SUCCESS
**
*******************************************************************************/
NFCSTATUS EXTNS_MfcRegisterNDefTypeHandler (tNFA_NDEF_CBACK *ndefHandlerCallback)
{

    NFCSTATUS status = NFCSTATUS_FAILED;
    if (NULL != ndefHandlerCallback)
    {
        gphNxpExtns_Context.p_ndef_cback = ndefHandlerCallback;
        status = NFCSTATUS_SUCCESS;
    }

    return status;
}

/*******************************************************************************
**                     Synchronizing Functions                                **
**            Synchronizes Callback in JNI and MFC Extns                      **
*******************************************************************************/

bool_t EXTNS_GetConnectFlag (void)
{
    return (gphNxpExtns_Context.ExtnsConnect);

}
void EXTNS_SetConnectFlag (bool_t flagval)
{
    gphNxpExtns_Context.ExtnsConnect = flagval;

}
bool_t EXTNS_GetDeactivateFlag(void)
{
   return (gphNxpExtns_Context.ExtnsDeactivate);

}
void EXTNS_SetDeactivateFlag (bool_t flagval)
{
    gphNxpExtns_Context.ExtnsDeactivate = flagval;

}
bool_t EXTNS_GetCallBackFlag(void)
{
   return (gphNxpExtns_Context.ExtnsCallBack);

}
void EXTNS_SetCallBackFlag (bool_t flagval)
{
    gphNxpExtns_Context.ExtnsCallBack = flagval;

}
NFCSTATUS EXTNS_GetPresenceCheckStatus (void)
{
    struct timespec ts;

    clock_gettime (CLOCK_REALTIME, &ts);
    ts.tv_sec += 0;
    ts.tv_nsec += 100*1000*1000; // 100 milliseconds
    if (ts.tv_nsec >= 1000 * 1000 * 1000)
    {
        ts.tv_sec += 1;
        ts.tv_nsec = ts.tv_nsec - (1000 * 1000 * 1000);
    }

    if (sem_timedwait (&gAuthCmdBuf.semPresenceCheck, &ts))
    {
        ALOGE("%s: failed to wait (errno=%d)", __func__, errno);
        sem_destroy (&gAuthCmdBuf.semPresenceCheck);
        gAuthCmdBuf.auth_sent = false;
        return NFCSTATUS_FAILED;
    }
    if (sem_destroy (&gAuthCmdBuf.semPresenceCheck))
    {
        ALOGE("%s: Failed to destroy check Presence semaphore (errno=%d)", __func__, errno);
    }
    return gAuthCmdBuf.status;
}

void MfcPresenceCheckResult (NFCSTATUS status)
{
    gAuthCmdBuf.status = status;
    EXTNS_SetCallBackFlag (true);
    sem_post (&gAuthCmdBuf.semPresenceCheck);
}
void MfcResetPresenceCheckStatus (void)
{
    gAuthCmdBuf.auth_sent = false;
}
/*******************************************************************************
**
** Function         EXTNS_CheckMfcResponse
**
** Description      This function is called from JNI Transceive for Mifare
**                  Classic Tag status interpretation and to send the required
**                  status to application
**
** Returns          NFCSTATUS_SUCCESS
**                  NFCSTATUS_FAILED
**
*******************************************************************************/
NFCSTATUS EXTNS_CheckMfcResponse (uint8_t** sTransceiveData, uint32_t *sTransceiveDataLen)
{
    NFCSTATUS status = NFCSTATUS_SUCCESS;

    if (*sTransceiveDataLen == 3)
    {
        if((*sTransceiveData) [0] == 0x10 && (*sTransceiveData) [1] != 0x0A)
        {
            NXPLOG_EXTNS_E ("Mifare Error in payload response");
            *sTransceiveDataLen = 0x1;
            *sTransceiveData += 1;
            return NFCSTATUS_FAILED;
        }
    }
    if ((*sTransceiveData) [0] == 0x40)
    {
        *sTransceiveData += 1;
        *sTransceiveDataLen = 0x01;
        if((*sTransceiveData) [0] == 0x03)
        {
            *sTransceiveDataLen = 0x00;
            status = NFCSTATUS_FAILED;
        }
    }
    else if ((*sTransceiveData) [0] == 0x10)
    {
        *sTransceiveData += 1;
        *sTransceiveDataLen = 0x10;
    }

    return status;
}
